package com.fury.managers;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fury.framework.FuryException;

/**
 * StateManager
 * 
 * @author Spiegel
 *
 * Latest Notes: 
 * Priority or time selection is not implemented (see chapter 2.2). I do not know where and when to 
 * use this. I will wait for a working example before adding this.
 * 
 */
public class StateManager implements ChangeDistributor{
	
	// TODO: Each request should be mapped by system, and same request from different systems 
		// should be processed at the same time
	
	
	
	//private TaskManaged taskManager;
	private Map <Long, List<MappedNotification>> cumulativeNotifyList;
	private Map <Long, Map<Subject, SubjectObservers>> threadObserverRequests;
	
	public StateManager() {
		threadObserverRequests = new HashMap <Long, Map<Subject, SubjectObservers>>();
	}

	public void unregisterThread() {

		threadObserverRequests.remove(Thread.currentThread().getId());
		cumulativeNotifyList.remove(Thread.currentThread().getId());
	}

	
	// In order to speed things up, a thread must register before adding new observers
	// this way we skip thousand of checks to see if the thread is in the map 
	public void registerThread() throws FuryException {
		
		threadObserverRequests.put(Thread.currentThread().getId(), new HashMap<Subject, SubjectObservers>() );
		cumulativeNotifyList.put(Thread.currentThread().getId(), new LinkedList<MappedNotification>());
	}
	
	public void register(Observer observer, Subject subject, String msg) {

		Map<Subject, SubjectObservers> subjectMap = threadObserverRequests.get(Thread.currentThread().getId());
		
		SubjectObservers observers = subjectMap.get(subject);
		
		if ( observers == null){
			// if subject is not already under observation, put it on observation
			observers = new SubjectObservers(subject);
			
			subjectMap.put(subject, observers);
			
			// observe the subject
			subject.registerChangeDistributor( this );
		}
		
		// add observer to subject
		observers.addMsgObserver(msg, observer);
	}

	public void distributeQueuedChanges() {
		// Merge the queues determining the type of synchonization
		List<MappedNotification> changeNotifications = mergeDataAllQueues();
		
		// Apply all different scene changes
		for(MappedNotification change : changeNotifications){
			applyChange(change);
		}
		
		// clean up the queues
		Iterator<Long> threadIter = threadObserverRequests.keySet().iterator();
		
		while(threadIter.hasNext()){
			Long id = threadIter.next();
			
			threadObserverRequests.get(id).clear();
		}
	}

	private List<MappedNotification> mergeDataAllQueues() {
		
		List<MappedNotification> notifications = new LinkedList<MappedNotification>();
		// TODO Add merging data changes
		return notifications;
	}

	private void applyChange(MappedNotification change) {
		/*
		List<Observer> observers = subjectMap.get(subject).getObserversForMessage(msg);
		// Create the mappedNotificatons
		MappedNotification notification = new MappedNotification(subject, msg, observers);
		
		cumulativeNotifyList.get(Thread.currentThread().getId()).add(notification);
		*/
	}

	public void setTaskManager() {

	}

	public void resetTaskManager() {

	}

	@Override
	public void updateChanges(Subject subject, String msg) {
		// Obtain the list of observers
		Map<Subject, SubjectObservers> subjectMap = threadObserverRequests.get(Thread.currentThread().getId());
		
		subjectMap.get(subject).setUpdateMesssage(msg);
		/*
		
		*/
	}
	
	/**********************************************************/

	private class SubjectObservers {
		private Subject subject;
		private Map<String, List<Observer>> observers;
		private Map<String, Object> msgsToUpdate;
		
		public SubjectObservers(Subject subject){
			this.subject = subject;
			observers = new HashMap<String, List<Observer>>();
			msgsToUpdate = new HashMap<String, Object>();
		}
		
		public final Subject getSubject() {
			return subject;
		}
		
		public final List<Observer> getObserversForMessage(String msg){
			return observers.get(msg);
		}
		
		public final void addMsgObserver(String msg, Observer observer){
			List<Observer> msgObservers = getObserversForMessage(msg);
			if(msgObservers == null){
				msgObservers = new LinkedList<Observer>();
				observers.put(msg, msgObservers);
			}
			
			msgObservers.add(observer);
		}
		
		public final void removeMsgObserver(String msg, Observer observer){
			List<Observer> msgObservers = getObserversForMessage(msg);
			if(msgObservers != null){
				observers.remove(observer);
			}
		}
		
		public final void setUpdateMesssage(String msg){
			msgsToUpdate.put(msg, null);
		}
		
		public Set<String> setUpdateMesssages(){
			return msgsToUpdate.keySet();
		}
		
	}

	private class MappedNotification {
		String msg;
		Subject subject;
		List<Observer> observers;
		
		public MappedNotification(Subject subject, String msg, List<Observer> observers){
			this.subject = subject;
			this.msg = msg;
			this.observers = observers;
		}
		
		public final String getMsg() {
			return msg;
		}
		public final void setMsg(String msg) {
			this.msg = msg;
		}
		public final Subject getSubject() {
			return subject;
		}
		public final void setSubject(Subject subject) {
			this.subject = subject;
		}
		public final List<Observer> getObservers() {
			return observers;
		}
		public final void setObservers(List<Observer> observers) {
			this.observers = observers;
		}
	}

}
